package ro.pasvante.mediaremote;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;

import android.app.Activity;
//import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TextView;

public class MediaRemote extends Activity {
	
	private static String MPC_COMMAND_PATH = "/command.html";
	private static String MPC_CALIB_LOOKUP = "<form action=\"/command.html\" method=\"post\">";
	private String MPC_COMMAND_STRING = "wm_command";
	private String MPC_COMMAND_BUTTON_NAME = "submit"; 
	private String MPC_COMMAND_BUTTON_VALUE = "Go!"; 

	// WARNING: these strings (MPC_CALIB_*_KEY) must be lower-case
	private static String MPC_CALIB_PLAYPAUSE_KEY = ">play/pause<";
	private static String MPC_CALIB_LISTPREV_KEY = ">previous playlist item<";
	private static String MPC_CALIB_LISTNEXT_KEY = ">next playlist item<";
	private String MPC_COMMAND_CODE_PLAYPAUSE = null;
	private String MPC_COMMAND_CODE_LISTPREV = null;
	private String MPC_COMMAND_CODE_LISTNEXT = null;

	private static String VLC_CALIB_LOOKUP = "* interface actions";

	// WARNING: these strings (VLC_CALIB_*_KEY) must be lower-case
	private static String VLC_CALIB_PLAYPAUSE_KEY = "function pl_pause()";
	private static String VLC_CALIB_LISTPREV_KEY = "function pl_previous()";
	private static String VLC_CALIB_LISTNEXT_KEY = "function pl_next()";
	private String VLC_COMMAND_CODE_PLAYPAUSE = null;
	private String VLC_COMMAND_CODE_LISTNEXT = null;
	private String VLC_COMMAND_CODE_LISTPREV = null;

	private static int HTTP_GET_CONN_TIMEOUT_MS = 1000;
	private static String HTTP_PROTOCOL_STRING = "http://";
	private static String PREF_SERVER_URL = "PREF_SERVER_URL";
	private static String PREF_SERVER_PORT_MPC = "PREF_SERVER_PORT_MPC";
	private static String PREF_SERVER_PORT_VLC = "PREF_SERVER_PORT_VLC";
	private static String PREF_SERVER_CHOSEN_MPC = "PREF_SERVER_CHOSEN_MPC";
	private static String SERVER_RESPONSE_ERROR = "Error comunicating with the server.";
	private static String COMMAND_CODE_ERROR = "Error: unable to get command codes from the server.";
	private static String COMMAND_CODE_SUCCESS = "Command successfully sent.";
	private String serverAddress = null;
	private String serverPortMPC = null;
	private String serverPortVLC = null;
	
	private OnClickListener mCommandButtonClicked = new OnClickListener() {		
		public void onClick(View v) {
			updateVarsFromEditTextControls();

			TextView out = (TextView)findViewById(R.id.TextView_output);
			
			RadioButton radio = (RadioButton)findViewById(R.id.RadioButton_mpc);
			
			if (radio.isChecked()) {
				out.setText(getResponseForCommandMPC(v.getId()));
			}
			else {
				out.setText(getResponseForCommandVLC(v.getId()));
			}
		}
	};
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		serverAddress = prefs.getString(PREF_SERVER_URL, "");
		serverPortMPC = prefs.getString(PREF_SERVER_PORT_MPC, getString(R.string.default_port_mpc));
		serverPortVLC = prefs.getString(PREF_SERVER_PORT_VLC, getString(R.string.default_port_vlc));
		Boolean isServerChosenMPC = prefs.getBoolean(PREF_SERVER_CHOSEN_MPC, true);
			
        setContentView(R.layout.main);

        Button button = (Button)findViewById(R.id.Button_play_pause); // this function has to be after the call to setContentView() 
        button.setOnClickListener(mCommandButtonClicked);

        button = (Button)findViewById(R.id.Button_playlist_prev); // this function has to be after the call to setContentView() 
        button.setOnClickListener(mCommandButtonClicked);

        button = (Button)findViewById(R.id.Button_playlist_next); // this function has to be after the call to setContentView() 
        button.setOnClickListener(mCommandButtonClicked);

        EditText editCtrl = (EditText)findViewById(R.id.EditText_server);
		editCtrl.setText(serverAddress);
		editCtrl = (EditText)findViewById(R.id.EditText_port_mpc);
		editCtrl.setText(serverPortMPC);
		editCtrl = (EditText)findViewById(R.id.EditText_port_vlc);
		editCtrl.setText(serverPortVLC);
		
		((RadioButton)findViewById(R.id.RadioButton_mpc)).setChecked(isServerChosenMPC);
		((RadioButton)findViewById(R.id.RadioButton_vlc)).setChecked(!isServerChosenMPC);
    }
    
    private static String getDataFromURL(String sUrl) {
    	StringBuffer buff = new StringBuffer();
    	String line;
    	try {
    		URL url = new URL(sUrl);
    		
    		URLConnection conn = url.openConnection();
    		
    		conn.setConnectTimeout(HTTP_GET_CONN_TIMEOUT_MS);
    		conn.setReadTimeout(HTTP_GET_CONN_TIMEOUT_MS);

    		BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    		while ((line = in.readLine()) != null) {
    			buff.append(line);
    	}
    	} catch (Exception e) {
    		return null;
    	}
    	return buff.toString();
    }
    
    private static String postDataToURL(String sUrl, List<NameValuePair> data) {
        // Create a new HttpClient and Post Header
        HttpClient httpclient = new DefaultHttpClient();
        String result = null;
        
        try {
            HttpResponse response = null;
            HttpPost httppost = new HttpPost(sUrl);
            httppost.setEntity(new UrlEncodedFormEntity(data));
            httppost.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); // Disable expect-continue, MPC server doesn't like this

            // Execute HTTP Post Request
            response = httpclient.execute(httppost);
            if (null == response) {
            	return null;
            }
            //result = response.getStatusLine().toString();
            result = EntityUtils.toString(response.getEntity());
            
            } catch (Exception e) {
            // TODO Auto-generated catch block
        }
        return result;
    } 

	@Override
	protected void onPause() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		SharedPreferences.Editor spe = prefs.edit();
		EditText editCtrl = (EditText)findViewById(R.id.EditText_server);
		spe.putString(PREF_SERVER_URL, editCtrl.getText().toString());
		editCtrl = (EditText)findViewById(R.id.EditText_port_mpc);
		spe.putString(PREF_SERVER_PORT_MPC, editCtrl.getText().toString());
		editCtrl = (EditText)findViewById(R.id.EditText_port_vlc);
		spe.putString(PREF_SERVER_PORT_VLC, editCtrl.getText().toString());
		spe.putBoolean(PREF_SERVER_CHOSEN_MPC, ((RadioButton)findViewById(R.id.RadioButton_mpc)).isChecked());
		spe.commit(); 
		
		super.onPause();
	}
	
	private String getMPCCommandCodeFromHtml(String htmlCode, String key) {
	    int keyPosInResult = htmlCode.indexOf(key);
	    if (20 > keyPosInResult) {
	    	// unexpected result
	    	return null;
	    }
	    int commaEnd = htmlCode.lastIndexOf('"', keyPosInResult);
	    if (-1 == commaEnd) {
	    	// unexpected result
	    	return null;
	    }
	    int commaStart = htmlCode.lastIndexOf('"', commaEnd - 1);
	    if (-1 == commaStart || commaEnd - commaStart < 1) {
	    	// unexpected result
	    	return null;
	    }
	    return htmlCode.substring(commaStart + 1, commaEnd);
	}
	
	private void readMPCCommandCodesFromServer() {
	    MPC_COMMAND_CODE_PLAYPAUSE = null;
	    MPC_COMMAND_CODE_LISTNEXT = null;
	    MPC_COMMAND_CODE_LISTPREV = null;
	    String htmlText = getDataFromURL("http://" + serverAddress + ":" + serverPortMPC + "/");
	    if (null == htmlText) {
	    	return;
	    }
	    htmlText = htmlText.toLowerCase();
	    if (-1 == htmlText.indexOf(MPC_CALIB_LOOKUP)) {
	    	// unexpected result
	    	return;
	    }
	    
	    MPC_COMMAND_CODE_PLAYPAUSE = getMPCCommandCodeFromHtml(htmlText, MPC_CALIB_PLAYPAUSE_KEY);

	    MPC_COMMAND_CODE_LISTNEXT = getMPCCommandCodeFromHtml(htmlText, MPC_CALIB_LISTNEXT_KEY);

	    MPC_COMMAND_CODE_LISTPREV = getMPCCommandCodeFromHtml(htmlText, MPC_CALIB_LISTPREV_KEY);
	}

	private String getVLCCommandCodeFromHtml(String htmlCode, String key) {
	    int keyPosInResult = htmlCode.indexOf(key);
	    if (-1 == keyPosInResult) {
	    	// unexpected result
	    	return null;
	    }
	    int functionStart = htmlCode.indexOf("loadxmldoc(", keyPosInResult);
	    if (-1 == functionStart) {
	    	// unexpected result
	    	return null;
	    }
	    int tickStart = htmlCode.indexOf('\'', functionStart);
	    if (-1 == tickStart) {
	    	// unexpected result
	    	return null;
	    }
	    int tickEnd = htmlCode.indexOf('\'', tickStart + 1);
	    if (-1 == tickEnd) {
	    	// unexpected result
	    	return null;
	    }
	    int andPos = htmlCode.indexOf('&', tickStart);

	    if (-1 != andPos && andPos < tickEnd) {
	    	return htmlCode.substring(tickStart + 1, andPos);
	    }
	    return htmlCode.substring(tickStart + 1, tickEnd);
	}
	
	private void readVLCCommandCodesFromServer() {
	    VLC_COMMAND_CODE_PLAYPAUSE = null;
	    VLC_COMMAND_CODE_LISTNEXT = null;
	    VLC_COMMAND_CODE_LISTPREV = null;
	    String htmlText = getDataFromURL("http://" + serverAddress + ":" + serverPortVLC + "/js/functions.js");
	    if (null == htmlText) {
	    	return;
	    }
	    htmlText = htmlText.toLowerCase();
	    if (-1 == htmlText.indexOf(VLC_CALIB_LOOKUP)) {
	    	// unexpected result
	    	return;
	    }
	    
	    VLC_COMMAND_CODE_PLAYPAUSE = getVLCCommandCodeFromHtml(htmlText, VLC_CALIB_PLAYPAUSE_KEY);

	    VLC_COMMAND_CODE_LISTNEXT = getVLCCommandCodeFromHtml(htmlText, VLC_CALIB_LISTNEXT_KEY);

	    VLC_COMMAND_CODE_LISTPREV = getVLCCommandCodeFromHtml(htmlText, VLC_CALIB_LISTPREV_KEY);
	}
	
	private void updateVarsFromEditTextControls() {
		EditText editCtrl = (EditText)findViewById(R.id.EditText_server);
		serverAddress = editCtrl.getText().toString();
		editCtrl = (EditText)findViewById(R.id.EditText_port_mpc);
		serverPortMPC = editCtrl.getText().toString();
		editCtrl = (EditText)findViewById(R.id.EditText_port_vlc);
		serverPortVLC = editCtrl.getText().toString();
	}
	
	private String getResponseForCommandMPC(int buttonID) {
		if (
				null == MPC_COMMAND_CODE_PLAYPAUSE ||
				null == MPC_COMMAND_CODE_LISTPREV ||
				null == MPC_COMMAND_CODE_LISTNEXT
				) {
			// un-calibrated
			readMPCCommandCodesFromServer();
		}
		
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

		switch (buttonID) {
		case R.id.Button_play_pause:
			if (null == MPC_COMMAND_CODE_PLAYPAUSE) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    nameValuePairs.add(new BasicNameValuePair(MPC_COMMAND_STRING, MPC_COMMAND_CODE_PLAYPAUSE));
			break;
		case R.id.Button_playlist_prev:
			if (null == MPC_COMMAND_CODE_LISTPREV) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    nameValuePairs.add(new BasicNameValuePair(MPC_COMMAND_STRING, MPC_COMMAND_CODE_LISTPREV));
			break;
		case R.id.Button_playlist_next:
			if (null == MPC_COMMAND_CODE_LISTNEXT) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    nameValuePairs.add(new BasicNameValuePair(MPC_COMMAND_STRING, MPC_COMMAND_CODE_LISTNEXT));
			break;
		}

	    nameValuePairs.add(new BasicNameValuePair(MPC_COMMAND_BUTTON_NAME, MPC_COMMAND_BUTTON_VALUE));

	    String response = postDataToURL(HTTP_PROTOCOL_STRING + serverAddress + ":" + serverPortMPC + MPC_COMMAND_PATH, nameValuePairs);

		if (null != response) {
			return COMMAND_CODE_SUCCESS;
	    }
	    return SERVER_RESPONSE_ERROR;
	}

	private String getResponseForCommandVLC(int buttonID) {
		if (
				null == VLC_COMMAND_CODE_PLAYPAUSE ||
				null == VLC_COMMAND_CODE_LISTPREV ||
				null == VLC_COMMAND_CODE_LISTNEXT
				) {
			// un-calibrated
			readVLCCommandCodesFromServer();
		}

		String response = null;
		String commandPart = HTTP_PROTOCOL_STRING + serverAddress + ":" + serverPortVLC + "/";  

		switch (buttonID) {
		case R.id.Button_play_pause:
			if (null == VLC_COMMAND_CODE_PLAYPAUSE) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    response = getDataFromURL(commandPart + VLC_COMMAND_CODE_PLAYPAUSE);
			break;
		case R.id.Button_playlist_prev:
			if (null == VLC_COMMAND_CODE_LISTPREV) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    response = getDataFromURL(commandPart + VLC_COMMAND_CODE_LISTPREV);
			break;
		case R.id.Button_playlist_next:
			if (null == VLC_COMMAND_CODE_LISTNEXT) {
				// calibration failed
				return COMMAND_CODE_ERROR;
			}
		    response = getDataFromURL(commandPart + VLC_COMMAND_CODE_LISTNEXT);
			break;
		}

		if (null != response) {
			return COMMAND_CODE_SUCCESS;
	    }
	    return SERVER_RESPONSE_ERROR;
	}
}